package com.kenliang.toolslib.zookeeper.lock;

import com.kenliang.toolslib.zookeeper.config.ZKUtils;
import org.apache.zookeeper.ZooKeeper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

/**
 *
 * 获取锁tryLock():
 *          1.如果锁根节点数据为此线程名，即此线程持有锁（不同jvm线程名需不一样），线程持有数加一，线程继续执行
 *          2.每个线程在锁根节点下创建自己的临时序列节点
 *          3. 创建后的回调方法：获取所有锁根节点下的临时序列节点(zk.getChildren())
 *          4.获取children后的回调方法：
 *              4-1.序列最小的：获得锁（countDownLatch.countDown()，线程继续执行）,设置根节点数据为 count+threadName（为了锁可重入）
 *              4-2.不是序列最小的：watch序列号为前一个的节点（zk.exist()）,watcher中在节点delete事件中重新获取锁根节点下所有节点（zk.getChildren()）
 *                      4-2-x 如果exist失败，即前一个节点已不存在（exist回调中stat==null），即重新获取锁根节点下所有节点（zk.getChildren()）
*   释放锁unLock():
 *          1.如果锁线程持有数减后为0，则删除节点，置空锁根节点数据
 *          2. 如果锁线程持有数减后不为0，则更新锁根节点数据为新的线程持有数
 *
 *
 *
 *
 *   注意：
 *   1, 因为zk连接可能断掉，导致死锁，所以建立的是临时节点，连接一断，session消失，节点消失
 */


/**
 * @author: 马士兵教育
 * @create: 2019-09-20 21:21
 */
public class TestLock {


    ZooKeeper zk ;

    @Before
    public void conn (){
        zk  = ZKUtils.getZK();
    }

    @After
    public void close (){
        try {
            zk.close();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void lock(){

        for (int i = 0; i < 10; i++) {
            new Thread(){
                @Override
                public void run() {
                    WatchCallBack watchCallBack = new WatchCallBack();
                    watchCallBack.setZk(zk);
                    String threadName = Thread.currentThread().getName();
                    watchCallBack.setThreadName(threadName);
                    //每一个线程：
                    //抢锁
                    watchCallBack.tryLock();
                    //干活
                    System.out.println(threadName+" working...");

                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //锁可重入设置
                    reenLockTest();
                    //释放锁
                    watchCallBack.unLock();


                }
            }.start();
        }
        while(true){

        }


    }

    private void reenLockTest(){
        WatchCallBack watchCallBack = new WatchCallBack();
        watchCallBack.setZk(zk);
        String threadName = Thread.currentThread().getName();
        watchCallBack.setThreadName(threadName);
        watchCallBack.setThreadName(threadName);
        //每一个线程：
        //抢锁
        watchCallBack.tryLock();
        //干活
        System.out.println(threadName+" reenLock working...");
        //释放锁
        watchCallBack.unLock();
    }




}
